<!DOCTYPE html>
<html lang="en">
  <!--
  This app demonstrates using the Touch Events touch event types (touchstart,
  touchmove, touchcancel and touchend) for the following interaction:
   1. Single touch
   2. Two (simultaneous) touches
   3. More than two simultaneous touches
   4. 1-finger swipe
   5. 2-finger move/pinch/swipe
-->
  <head>
    <title>Touch Events tutorial</title>
    <meta name="viewport" content="width=device-width" />
    <style>
      div {
        margin: 0em;
        padding: 2em;
      }

      #target1,
      #target2,
      #target3,
      #target4 {
        background: white;
        border: 1px solid black;
      }
    </style>

    <script>
      // Log events flag
      var logEvents = false;

      // Touch Point cache
      var tpCache = new Array();

      function enableLog(ev) {
        logEvents = logEvents ? false : true;
      }

      function log(name, ev, printTargetIds) {
        var o = document.getElementsByTagName("output")[0];
        var s =
          name +
          ": touches = " +
          ev.touches.length +
          " ; targetTouches = " +
          ev.targetTouches.length +
          " ; changedTouches = " +
          ev.changedTouches.length;
        o.innerHTML += s + " <br>";

        if (printTargetIds) {
          s = "";
          for (var i = 0; i < ev.targetTouches.length; i++) {
            s += "... id = " + ev.targetTouches[i].identifier + " <br>";
          }
          o.innerHTML += s;
        }
      }

      function clearLog(event) {
        var o = document.getElementsByTagName("output")[0];
        o.innerHTML = "";
      }

      function update_background(ev) {
        // Change background color based on the number simultaneous touches
        // in the event's targetTouches list:
        //   yellow - one tap (or hold)
        //   pink - two taps
        //   lightblue - more than two taps
        switch (ev.targetTouches.length) {
          case 1:
            // Single tap`
            ev.target.style.background = "yellow";
            break;
          case 2:
            // Two simultaneous touches
            ev.target.style.background = "pink";
            break;
          default:
            // More than two simultaneous touches
            ev.target.style.background = "lightblue";
        }
      }

      // This is a very basic 2-touch move/pinch/zoom handler that does not include
      // error handling, only handles horizontal moves, etc.
      function handle_pinch_zoom(ev) {
        if (ev.targetTouches.length == 2 && ev.changedTouches.length == 2) {
          // Check if the two target touches are the same ones that started
          // the 2-touch
          var point1 = -1,
            point2 = -1;
          for (var i = 0; i < tpCache.length; i++) {
            if (tpCache[i].identifier == ev.targetTouches[0].identifier)
              point1 = i;
            if (tpCache[i].identifier == ev.targetTouches[1].identifier)
              point2 = i;
          }
          if (point1 >= 0 && point2 >= 0) {
            // Calculate the difference between the start and move coordinates
            var diff1 = Math.abs(
              tpCache[point1].clientX - ev.targetTouches[0].clientX
            );
            var diff2 = Math.abs(
              tpCache[point2].clientX - ev.targetTouches[1].clientX
            );

            // This threshold is device dependent as well as application specific
            var PINCH_THRESHHOLD = ev.target.clientWidth / 10;
            if (diff1 >= PINCH_THRESHHOLD && diff2 >= PINCH_THRESHHOLD)
              ev.target.style.background = "green";
          } else {
            // empty tpCache
            tpCache = new Array();
          }
        }
      }

      function start_handler(ev) {
        // If the user makes simultaneious touches, the browser will fire a
        // separate touchstart event for each touch point. Thus if there are
        // three simultaneous touches, the first touchstart event will have
        // targetTouches length of one, the second event will have a length
        // of two, and so on.
        ev.preventDefault();
        // Cache the touch points for later processing of 2-touch pinch/zoom
        if (ev.targetTouches.length == 2) {
          for (var i = 0; i < ev.targetTouches.length; i++) {
            tpCache.push(ev.targetTouches[i]);
          }
        }
        if (logEvents) log("touchStart", ev, true);
        update_background(ev);
      }

      function move_handler(ev) {
        // Note: if the user makes more than one "simultaneous" touches, most browsers
        // fire at least one touchmove event and some will fire several touchmoves.
        // Consequently, an application might want to "ignore" some touchmoves.
        //
        // This function sets the target element's outline to "dashed" to visualy
        // indicate the target received a move event.
        //
        ev.preventDefault();
        if (logEvents) log("touchMove", ev, false);
        // To avoid too much color flashing many touchmove events are started,
        // don't update the background if two touch points are active
        if (!(ev.touches.length == 2 && ev.targetTouches.length == 2))
          update_background(ev);

        // Set the target element's outline to dashed to give a clear visual
        // indication the element received a move event.
        ev.target.style.outline = "dashed";

        // Check this event for 2-touch Move/Pinch/Zoom gesture
        handle_pinch_zoom(ev);
      }

      function end_handler(ev) {
        ev.preventDefault();
        if (logEvents) log(ev.type, ev, false);
        if (ev.targetTouches.length == 0) {
          // Restore background and outline to original values
          ev.target.style.background = "white";
          ev.target.style.outline = "1px solid black";
        }
      }

      function set_handlers(name) {
        // Install event handlers for the given element
        var el = document.getElementById(name);
        el.ontouchstart = start_handler;
        el.ontouchmove = move_handler;
        // Use same handler for touchcancel and touchend
        el.ontouchcancel = end_handler;
        el.ontouchend = end_handler;
      }

      function init() {
        set_handlers("target1");
        set_handlers("target2");
        set_handlers("target3");
        set_handlers("target4");
      }
    </script>
  </head>
  <body onload="init();">
    <h1>Multi-touch interaction</h1>
    <!-- Create some boxes to test various gestures. -->
    <div id="target1">Tap, Hold or Swipe me 1</div>
    <div id="target2">Tap, Hold or Swipe me 2</div>
    <div id="target3">Tap, Hold or Swipe me 3</div>
    <div id="target4">Tap, Hold or Swipe me 4</div>

    <!-- UI for logging/debugging -->
    <button id="log" onclick="enableLog(event);">
      Start/Stop event logging
    </button>
    <button id="clearlog" onclick="clearLog(event);">Clear the log</button>
    <p></p>
    <output></output>
  </body>
</html>
